<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>EverRest Groovy Example</title>
</head>
<body>
<h1>EverRest Groovy Example</h1>

<p>This is sample of using Groovy language for creation JAX-RS components.</p>

<p>We will create simple books service. It should be able give access to books by id, get list all available books
    and add new book in storage. Service supports JSON format for transfer data to/from client. Groovy scripts will be
    compiled in runtime so we will not use maven groovy plugin to compile it and so on. In this example groovy code
    will be located in current project but it is not necessary. They may be located in any other place even at remote
    server.</p>
</body>
<ul>
<li>Add required <i>contex-param</i>.
      <pre>
      &lt;context-param&gt;
         &lt;param-name&gt;org.everrest.groovy.Application&lt;/param-name&gt;
         &lt;param-value&gt;org.everrest.sample.groovy.BookApplication&lt;/param-value&gt;   
      &lt;/context-param&gt;
      &lt;context-param&gt;
         &lt;param-name&gt;org.everrest.groovy.root.resources&lt;/param-name&gt;
         &lt;!-- <i>Can be absolute URL that locate to any place at local file system or remote (local) URL, e.g.
          http://localhost/~USER/public_html/... </i> --&gt;
         &lt;param-value&gt;file:./src/main/resources/&lt;/param-value&gt;
      &lt;/context-param&gt;
      </pre>
    <table border="1">
        <tbody>
        <tr>
            <th>Parameter</th>
            <th>Description</th>
        </tr>
        <tr>
            <td>org.everrest.groovy.Application</td>
            <td>This attribute has the same purpose as <i>javax.ws.rs.Application</i>. This is FQN of Groovy or Java class that
                extends <i>javax.ws.rs.core.Application</i> and provides set of classes and(or) instances of JAX-RS components.
            </td>
        </tr>
        <tr>
            <td>org.everrest.groovy.root.resources</td>
            <td>The purpose of this parameter is close to class-path. It is one or more URLs separated by comma that we be used
                as start point for lookup scripts.
            </td>
        </tr>
        </tbody>
    </table>
</li>
<li>Add bootstrap listeners.
    <p>Need add two listeners. First one initializes common components of EverRest frameworks. The second one starts
        Groovy application.</p>
      <pre>
      &lt;listener&gt;
         &lt;listener-class&gt;org.everrest.core.servlet.EverrestInitializedListener&lt;/listener-class&gt;
      &lt;/listener&gt;
      &lt;listener&gt;
         &lt;listener-class&gt;org.everrest.groovy.servlet.GroovyEverrestInitializedListener&lt;/listener-class&gt;
      &lt;/listener&gt;
      </pre>
</li>
<li>Add EverrestServlet.
      <pre>
      &lt;servlet&gt;
         &lt;servlet-name&gt;EverrestServlet&lt;/servlet-name&gt;
         &lt;servlet-class&gt;org.everrest.core.servlet.EverrestServlet&lt;/servlet-class&gt;
      &lt;/servlet&gt;
      &lt;servlet-mapping&gt;
         &lt;servlet-name&gt;EverrestServlet&lt;/servlet-name&gt;
         &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
      &lt;/servlet-mapping&gt;
      </pre>
</li>
<li>EverRest components.
    <p><i>org.everrest.sample.groovy.BookApplication.groovy</i> - application deployer. We decide to have all components
        as singleton.</p>
      <pre>
      class BookApplication extends javax.ws.rs.core.Application
      {
         Set&lt;Class&lt;?&gt;&gt; getClasses()
         {
           new HashSet&lt;Class&lt;?&gt;&gt;([])
         }

         Set&lt;Object&gt; getSingletons()
         {
            new HashSet&lt;Object&gt;([new BookService(bookStorage:new BookStorage()), new BookNotFoundExceptionMapper()])
         }
      }      
      </pre>
    <p><i>org.everrest.sample.groovy.Book.groovy</i> - contains simple Groovy Bean that will be used to transfer data
        via JSON.</p>

    <p><i>org.everrest.sample.groovy.BookNotFoundException.groovy</i> - exception that will be thrown by
        <i>org.everrest.sample.groovy.BookService.groovy</i> if client requested book that does not exist in storage.</p>

    <p><i>org.everrest.sample.groovy.BookNotFoundExceptionMapper.groovy</i> - JAX-RS component that intercepts
        <i>org.everrest.sample.groovy.BookNotFoundException.groovy</i> and send correct response to client.</p>
      <pre>
      @Provider
      class BookNotFoundExceptionMapper implements ExceptionMapper&lt;BookNotFoundException&gt;
      {
         Response toResponse(BookNotFoundException exception)
         {
            Response.status(404).entity(exception.getMessage()).type('text/plain').build()
         }
      }
      </pre>
    <p><i>org.everrest.sample.groovy.BookService.groovy</i> - JAX-RS service that process client's requests.</p>
      <pre>
      @Path('books')
      class BookService
      {
         BookStorage bookStorage

         @Path('{id}'
         @GET
         @Produces('application/json')
         Book get(@PathParam('id') String id) throws BookNotFoundException
         {
            Book book = bookStorage.getBook(id)
            if (book == null)
               throw new BookNotFoundException(id)
            book
         }
			   
         @GET
         @Produces('application/json')
         public Collection&lt;Book&gt; getAll()
         {
            bookStorage.getAll()
         }
			   
         @PUT
         @Consumes('application/json')
         public Response put(Book book, @Context UriInfo uriInfo)
         {
            String id = bookStorage.putBook(book)
            URI location = uriInfo.getBaseUriBuilder().path(getClass()).path(id).build()
            Response.created(location).entity(location.toString()).type('text/plain').build()
         }
      }
      </pre>
    <p><i>org.everrest.sample.groovy.BookStorage.groovy</i> - storage of Books.</p>
      <pre>
      class BookStorage
      {
         private static int idCounter = 100
         
         synchronized String generateId()
         {
            idCounter++
            Integer.toString(idCounter)
         }
         
         private Map books = new ConcurrentHashMap()
         
         BookStorage()
         {
            init()
         }
         
         private void init()
         {
            putBook(new Book(title:'JUnit in Action', author:'Vincent Masson', pages:386, price:19.37))
         }
         
         Book getBook(String id)
         {
            books[id]
         }
         
         String putBook(Book book)
         {
            String id = book.getId()
            if (id == null || id.trim().length() == 0)
            {
               id = generateId()
               book.setId(id)
            }
            books[id] = book
            id
         }
         
         Collection&lt;Book&gt; getAll()
         {
            books.values()
         }
         
         int numberOfBooks()
         {
            books.size()
         }
      }
      </pre>
</li>
<li>Request mapping.
    <table border="1">
        <tbody>
        <tr>
            <th>Relative Path</th>
            <th>HTTP Method</th>
            <th>Description</th>
        </tr>
        <tr>
            <td>groovy-book-service/books/{id}</td>
            <td>GET</td>
            <td>Get books with specified id. Just after server start only one book in storage and it can be accessed via
                id <i>101</i></td>
        </tr>
        <tr>
            <td>groovy-book-service/books/</td>
            <td>GET</td>
            <td>Get all books from storage.</td>
        </tr>
        <tr>
            <td>groovy-book-service/books/</td>
            <td>PUT</td>
            <td>Add new book in storage. The body of request must contains book's description in JSON format. The
                <i>Content-type</i> header must be set to <i>application/json</i></td>
        </tr>
        </tbody>
    </table>
</li>
<li>How to try.
    <p>Build project.</p>
    <pre>mvn clean install</pre>
    <p>Run it with Jetty server.</p>
    <pre>mvn jetty:run</pre>
    <p>Point you web browser to <a href="http://localhost:8080/groovy-book-service/books/101">http://localhost:8080/groovy-book-service/books/101</a>
    </p>

    <p>If you are under linux or other unix like OS the you can use <i>curl</i> utility (often it is already installed).
        Binary build of this utility available for windows also at <a href="http://curl.haxx.se/download.html">http://curl.haxx.se/download.html</a>.
        With <i>curl</i> you able to add new book in storage via command</p>
         <pre>
         curl -X PUT \
         -H "Content-type:application/json" \
         -d '{"author":"My Author","title":"My Title","price":1.00,"pages":100}' \
         http://localhost:8080/groovy-book-service/books/ 
         </pre>
</li>

</ul>
</html>